package jroguelike.utils;

import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: aimozg
 * Date: 01.08.11
 * Time: 4:11
 */
public class HashMultiMap<K, V> implements MultiMap<K, V> {

	private final HashMap<K, LinkedList<V>> map = new HashMap<>();

	private LinkedList<V> getList(K key) {
		LinkedList<V> val = map.get(key);
		if (val == null) {
			val = new LinkedList<>();
			map.put(key, val);
		}
		return val;
	}

	@Override
	public List<V> get(K key) {
		return getList(key);
	}

	@Override
	public V getFirst(K key) {
		LinkedList<V> val = map.get(key);
		if (val == null) return null;
		if (val.isEmpty()) return null;
		return val.getFirst();
	}

	@Override
	public void put(K key, V value) {
		getList(key).add(value);
	}

	@Override
	public void clear() {
		map.clear();
	}

	@Override
	public void clearKey(K key) {
		LinkedList<V> val = map.get(key);
		if (val == null) return;
		val.clear();
	}

	@Override
	public boolean remove(K key, V value) {
		LinkedList<V> val = map.get(key);
		if (val != null) {
			return val.remove(value);
		}
		return false;
	}

	@Override
	public int size() {
		int counter = 0;
		for (LinkedList<V> v : map.values()) {
			counter += v.size();
		}
		return counter;
	}

	@Override
	public boolean isEmpty() {
		return (size() == 0);
	}

	@Override
	public Iterator<Map.Entry<K, V>> iterator() {
		return new HMMIterator();
	}

	private class HMMEntry<K, V> implements Map.Entry<K, V> {

		private final K key;
		private final V value;

		private HMMEntry(K key, V value) {
			this.key = key;
			this.value = value;
		}

		@Override
		public K getKey() {
			return key;
		}

		@Override
		public V getValue() {
			return value;
		}

		@Override
		public V setValue(V value) {
			throw new UnsupportedOperationException();
		}
	}

	private class HMMIterator implements Iterator<Map.Entry<K, V>> {

		private Iterator<Map.Entry<K, LinkedList<V>>> mapIterator = map.entrySet().iterator();
		private K currentKey;
		private Iterator<V> listIterator = null;

		@Override
		public boolean hasNext() {
			while (true) {
				if (listIterator != null && listIterator.hasNext()) return true;
				// listIterator doesn't have next. check next listIterator
				if (mapIterator.hasNext()) {
					nextListIterator();
				} else {
					return false;
				}
			}
		}

		private void nextListIterator() {
			Map.Entry<K, LinkedList<V>> entry = mapIterator.next();
			currentKey = entry.getKey();
			listIterator = entry.getValue().iterator();
		}

		@Override
		public Map.Entry<K, V> next() {
			while (true) {
				if (listIterator != null && listIterator.hasNext())
					return new HMMEntry<>(currentKey, listIterator.next());
				if (mapIterator.hasNext()) {
					nextListIterator();
				} else {
					throw new NoSuchElementException();
				}
			}
		}

		@Override
		public void remove() {
			listIterator.remove();
		}
	}
}
